﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WJP.Logging
{
    [Serializable]
    public abstract class AbstractLogger : ILogger, IHasLogLevel
    {
        public LogLevel Level { get; set; }

        public string Name { get; protected set; }

        protected AbstractLogger() : this("unnamed", LogLevel.Off)
        {
            
        }

        protected AbstractLogger(string name) : this(name, LogLevel.Off)
        {
            
        }

        protected AbstractLogger(LogLevel logSeverity) : this("unnamed", logSeverity)
        {
            
        }

        protected AbstractLogger(string name, LogLevel logSeverity)
        {
            ChangeName(name);
            Level = logSeverity;
        }

        protected void ChangeName(string newName)
        {
            Name = newName ?? throw new ArgumentNullException(nameof(newName));
        }

        public abstract ILogger CreateChildLogger(string loggerName);

        #region ILogger实现

        #region IsEnabled

        public bool IsDebugEnabled => Level >= LogLevel.Debug;

        public bool IsInfoEnabled => Level >= LogLevel.Info;

        public bool IsWarnEnabled => Level >= LogLevel.Warn;

        public bool IsErrorEnabled => Level >= LogLevel.Error;

        public bool IsFatalEnabled => Level >= LogLevel.Fatal;

        #endregion

        public void Debug(string message)
        {
            Debug(message, null);
        }

        public void Debug(string message, Exception exception)
        {
            if (!IsDebugEnabled)
                return;

            Log(LogLevel.Debug, message, exception);
        }

        public void DebugFormat(string format, params object[] args)
        {
            DebugFormat((Exception)null, format, args);
        }

        public void DebugFormat(Exception exception, string format, params object[] args)
        {
            if (!IsDebugEnabled)
                return;

            Log(LogLevel.Debug, string.Format(CultureInfo.CurrentCulture, format, args), exception);
        }

        public void DebugFormat(IFormatProvider formatProvider, string format, params object[] args)
        {
            DebugFormat(null, formatProvider, format, args);
        }

        public void DebugFormat(Exception exception, IFormatProvider formatProvider, string format, params object[] args)
        {
            if (!IsDebugEnabled)
                return;

            Log(LogLevel.Debug, string.Format(formatProvider, format, args), exception);
        }

        public void Info(string message)
        {
            Info(message, null);
        }

        public void Info(string message, Exception exception)
        {
            if (!IsInfoEnabled)
                return;

            Log(LogLevel.Info, message, exception);
        }

        public void InfoFormat(string format, params object[] args)
        {
            InfoFormat((Exception)null, format, args);
        }

        public void InfoFormat(Exception exception, string format, params object[] args)
        {
            if (!IsInfoEnabled)
                return;

            Log(LogLevel.Info, string.Format(CultureInfo.CurrentCulture, format, args), exception);
        }

        public void InfoFormat(IFormatProvider formatProvider, string format, params object[] args)
        {
            InfoFormat(null, formatProvider, format, args);
        }

        public void InfoFormat(Exception exception, IFormatProvider formatProvider, string format, params object[] args)
        {
            if (!IsInfoEnabled)
                return;

            Log(LogLevel.Info, string.Format(formatProvider, format, args), exception);
        }

        public void Warn(string message)
        {
            Warn(message, null);
        }

        public void Warn(string message, Exception exception)
        {
            if (!IsWarnEnabled)
                return;

            Log(LogLevel.Warn, message, exception);
        }

        public void WarnFormat(string format, params object[] args)
        {
            WarnFormat((Exception)null, format, args);
        }

        public void WarnFormat(Exception exception, string format, params object[] args)
        {
            if (!IsWarnEnabled)
                return;

            Log(LogLevel.Warn, string.Format(CultureInfo.CurrentCulture, format, args), exception);
        }

        public void WarnFormat(IFormatProvider formatProvider, string format, params object[] args)
        {
            WarnFormat(null, formatProvider, format, args);
        }

        public void WarnFormat(Exception exception, IFormatProvider formatProvider, string format, params object[] args)
        {
            if (!IsWarnEnabled)
                return;

            Log(LogLevel.Warn, string.Format(formatProvider, format, args), exception);
        }

        public void Error(string message)
        {
            Error(message, null);
        }

        public void Error(string message, Exception exception)
        {
            if (!IsErrorEnabled)
                return;

            Log(LogLevel.Error, message, exception);
        }

        public void ErrorFormat(string format, params object[] args)
        {
            ErrorFormat((Exception)null, format, args);
        }

        public void ErrorFormat(Exception exception, string format, params object[] args)
        {
            if (!IsErrorEnabled)
                return;

            Log(LogLevel.Error, string.Format(CultureInfo.CurrentCulture, format, args), exception);
        }

        public void ErrorFormat(IFormatProvider formatProvider, string format, params object[] args)
        {
            ErrorFormat(null, formatProvider, format, args);
        }

        public void ErrorFormat(Exception exception, IFormatProvider formatProvider, string format, params object[] args)
        {
            if (!IsErrorEnabled)
                return;

            Log(LogLevel.Error, string.Format(formatProvider, format, args), exception);
        }

        public void Fatal(string message)
        {
            Fatal(message, null);
        }

        public void Fatal(string message, Exception exception)
        {
            if (!IsFatalEnabled)
                return;

            Log(LogLevel.Fatal, message, exception);
        }

        public void FatalFormat(string format, params object[] args)
        {
            FatalFormat((Exception)null, format, args);
        }

        public void FatalFormat(Exception exception, string format, params object[] args)
        {
            if (!IsFatalEnabled)
                return;

            Log(LogLevel.Fatal, string.Format(CultureInfo.CurrentCulture, format, args), exception);
        }

        public void FatalFormat(IFormatProvider formatProvider, string format, params object[] args)
        {
            FatalFormat(null, formatProvider, format, args);
        }

        public void FatalFormat(Exception exception, IFormatProvider formatProvider, string format, params object[] args)
        {
            if (!IsFatalEnabled)
                return;

            Log(LogLevel.Fatal, string.Format(formatProvider, format, args), exception);
        }

        #endregion

        /// <summary>
        /// 只要实现这个日志记录方法
        /// <para><see cref="Exception"/>可以为空</para>
        /// </summary>
        /// <param name="level">日志等级</param>
        /// <param name="loggerName">日志记录器名称</param>
        /// <param name="message">日志内容</param>
        /// <param name="exception">错误信息</param>
        protected abstract void Log(LogLevel level, string loggerName, string message, Exception exception);

        private void Log(LogLevel level, string message, Exception exception)
        {
            Log(level, Name, message, exception);
        }
    }
}
